package com.example.base_model.socket

import android.content.Context
import com.blankj.utilcode.util.LogUtils
import com.squareup.moshi.Moshi
import info.mqtt.android.service.MqttAndroidClient
import info.mqtt.android.service.QoS
import jakarta.inject.Inject
import jakarta.inject.Singleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions
import org.eclipse.paho.client.mqttv3.IMqttActionListener
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken
import org.eclipse.paho.client.mqttv3.IMqttToken
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended
import org.eclipse.paho.client.mqttv3.MqttConnectOptions
import org.eclipse.paho.client.mqttv3.MqttMessage

/**
 * @Author: jiangKunKun
 * @CreateDate: 2025/11/24
 * @Description:   现代化的MQTT管理器
 *                  使用Hilt进行依赖注入，支持Flow、协程和指数退避重连机制
 *                  提供完整的MQTT客户端功能：连接、断开、订阅、发布等
 * @param context Android应用上下文，用于创建MQTT客户端
 * @param moshi JSON序列化工具，用于对象与JSON之间的转换
 * @SpittingGrooves:
 */
@Singleton
class ModernMqttManager @Inject constructor(
    private val context: Context,
    val moshi: Moshi
) {
    /**
     * MQTT Android客户端实例
     * 用于执行所有MQTT协议相关的操作
     */
    private var mqttAndroidClient: MqttAndroidClient? = null

    /**
     * MQTT连接配置信息
     * 存储当前MQTT连接的各项配置参数
     */
    private var config: MqttConfig? = null

    /**
     * 当前订阅的主题映射表
     * key: 主题名称, value: 服务质量等级(QoS)
     * 用于重连后自动重新订阅
     */
    private val subscriptions = mutableMapOf<String, Int>()

    /**
     * 是否应该继续重连标志位
     * 控制重连循环的终止条件
     */
    private var shouldReconnect = true

    /**
     * MQTT消息流
     * 通过SharedFlow提供线程安全的消息广播机制
     * 业务层可通过collect方法监听所有MQTT消息
     */
    private val _messageFlow = MutableSharedFlow<MqttMessageWrapper>()

    /**
     * 公开的消息流接口
     * 只读Flow，防止外部修改内部消息流
     */
    val messageFlow: SharedFlow<MqttMessageWrapper> = _messageFlow.asSharedFlow()

    /**
     * MQTT连接状态流
     * 用于广播MQTT客户端的连接状态变化
     */
    private val _connectionState = MutableStateFlow(MqttConnectionState.DISCONNECTED)

    /**
     * 公开的连接状态流接口
     * 业务层可通过collect监听连接状态变化
     */
    val connectionState: StateFlow<MqttConnectionState> = _connectionState.asStateFlow()

    /**
     * 初始重连延迟时间（毫秒）
     * 第一次重连等待1秒
     */
    private companion object {
        private const val INITIAL_DELAY = 1_000L // 1秒

        /**
         * 最大重连延迟时间（毫秒）
         * 最长等待60秒进行重连尝试
         */
        private const val MAX_DELAY = 60_000L    // 60秒

        /**
         * 断开连接时的消息缓冲区大小
         * 缓冲最多100条未发送的消息
         */
        private const val BUFFER_SIZE = 100
    }

    /**
     * 初始化MQTT客户端
     * 创建MQTT客户端实例并设置回调处理器
     *
     * @param config MQTT配置信息
     * @throws IllegalArgumentException 当config为空时抛出异常
     */
    fun initialize(config: MqttConfig) {
        // 保存配置信息供后续使用
        this.config = config
        val applicationContext = context.applicationContext

        // 创建MQTT客户端实例
        mqttAndroidClient = MqttAndroidClient(
            applicationContext,
            config.serverUri,
            config.clientId
        ).apply {
            // 设置MQTT回调处理器
            setCallback(mqttCallback)
        }
    }

    /**
     * 设置断开连接时的缓冲选项
     * 配置客户端在断开连接期间的消息缓存行为
     */
    private fun setupDisconnectedBuffer() {
        // 创建断开连接缓冲区配置
        val bufferOptions = DisconnectedBufferOptions().apply {
            // 启用缓冲区功能
            isBufferEnabled = true
            // 设置缓冲区大小
            bufferSize = BUFFER_SIZE
            // 不持久化缓冲区内容
            isPersistBuffer = false
            // 不删除最旧消息，当缓冲区满时拒绝新消息
            isDeleteOldestMessages = false
        }
        // 应用缓冲区配置到MQTT客户端
        mqttAndroidClient?.setBufferOpts(bufferOptions)
    }

    /**
     * MQTT回调处理器
     * 处理MQTT客户端的各种事件回调
     */
    private val mqttCallback = object : MqttCallbackExtended {
        /**
         * 连接完成回调
         * 当MQTT客户端成功连接到服务器时调用
         *
         * @param reconnect 是否为重连操作
         * @param serverURI 实际连接的服务器地址
         */
        override fun connectComplete(reconnect: Boolean, serverURI: String) {
            // 更新连接状态为已连接
            _connectionState.value = MqttConnectionState.CONNECTED
            setupDisconnectedBuffer()
            // 如果是重连，则重新订阅之前订阅的主题
            resubscribeTopics()
        }

        /**
         * 连接丢失回调
         * 当MQTT客户端与服务器失去连接时调用
         *
         * @param cause 连接丢失的原因
         */
        override fun connectionLost(cause: Throwable?) {
            // 更新连接状态为已断开
            _connectionState.value = MqttConnectionState.DISCONNECTED
            // 如果启用了自动重连并且允许重连，则启动重连流程
            if (shouldReconnect && config?.automaticReconnect == true) {
                CoroutineScope(Dispatchers.IO).launch {
                    reconnectWithExponentialBackoff()
                }
            }
        }

        /**
         * 消息到达回调
         * 当订阅的主题有新消息到达时调用
         *
         * @param topic 消息来源的主题
         * @param message 接收到的MQTT消息对象
         */
        override fun messageArrived(topic: String, message: MqttMessage) {
            // 将字节数组转换为UTF-8字符串
            val payload = String(message.payload, Charsets.UTF_8)
            // 通过协程作用域启动新协程发送消息到Flow
            CoroutineScope(Dispatchers.IO).launch {
                // 将消息包装后发送到消息流
                // TODO 待封装消息体
                _messageFlow.emit(MqttMessageWrapper(topic, payload))
            }
            // 同时通过LiveEventBus发送（如果项目中有使用）
            // LiveEventBus.get(topic).post(payload)
        }

        /**
         * 消息投递完成回调
         * 当消息成功发送到服务器时调用
         *
         * @param token 消息投递令牌
         */
        override fun deliveryComplete(token: IMqttDeliveryToken) {
            // 消息发送完成回调，通常不需要特殊处理
        }
    }

    /**
     * 连接到MQTT服务器
     * 建立与MQTT服务器的网络连接
     *
     * @return 连接是否成功
     */
    suspend fun connect(): Boolean = withContext(Dispatchers.IO) {
        // 获取当前配置，如果为空则返回false
        val currentConfig = config ?: return@withContext false
        // 获取MQTT客户端实例，如果为空则返回false
        val client = mqttAndroidClient ?: return@withContext false

        return@withContext try {
            // 创建MQTT连接选项
            val options = MqttConnectOptions().apply {
                // 设置自动重连
                isAutomaticReconnect = currentConfig.automaticReconnect
                // 设置清理会话
                isCleanSession = currentConfig.cleanSession
                // 设置用户名
                userName = currentConfig.username
                // 设置密码
                password = currentConfig.password.toCharArray()
                // 设置连接超时时间
                connectionTimeout = currentConfig.connectionTimeout
                // 设置心跳间隔
                keepAliveInterval = currentConfig.keepAliveInterval
            }

            // 执行连接操作
            val token = client.connect(options)
            // 设置连接结果回调
            token.actionCallback = object : IMqttActionListener {
                /**
                 * 连接成功回调
                 */
                override fun onSuccess(asyncActionToken: IMqttToken) {
                    // 更新连接状态为已连接
                    _connectionState.value = MqttConnectionState.CONNECTED
                }

                /**
                 * 连接失败回调
                 *
                 * @param asyncActionToken 异步操作令牌
                 * @param exception 连接失败异常
                 */
                override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                    // 更新连接状态为已断开
                    _connectionState.value = MqttConnectionState.DISCONNECTED
                    // 如果启用了自动重连并且允许重连，则启动重连流程
                    if (shouldReconnect && currentConfig.automaticReconnect) {
                        CoroutineScope(Dispatchers.IO).launch {
                            reconnectWithExponentialBackoff()
                        }
                    }
                }
            }

            // 等待连接操作完成
            token.waitForCompletion()
            true
        } catch (e: Exception) {
            // 连接过程中发生异常，返回false
            false
        }
    }

    /**
     * 指数退避重连机制
     * 实现智能重连策略，避免频繁重连造成服务器压力
     */
    private suspend fun reconnectWithExponentialBackoff() {
        // 初始化延迟时间为初始值
        var delayMillis = INITIAL_DELAY

        // 循环尝试重连直到成功或被取消
        while (shouldReconnect && !isConnected()) {
            try {
                // 尝试连接
                if (connect()) {
                    // 连接成功，重置延迟时间
                    delayMillis = INITIAL_DELAY
                } else {
                    // 连接失败，等待指定时间后重试
                    delay(delayMillis)
                    // 指数增长延迟时间，但不超过最大值
                    delayMillis = minOf(delayMillis * 2, MAX_DELAY)
                }
            } catch (e: Exception) {
                // 发生异常也等待指定时间后重试
                delay(delayMillis)
                // 指数增长延迟时间，但不超过最大值
                delayMillis = minOf(delayMillis * 2, MAX_DELAY)
            }
        }
    }

    /**
     * 订阅指定主题
     * 开始接收指定主题的消息
     *
     * @param topic 要订阅的主题
     * @param qos 服务质量等级，决定消息传递保证级别
     * @return 订阅是否成功
     */
    suspend fun subscribe(topic: String, qos: Int = QoS.AtMostOnce.value): Boolean =
        withContext(Dispatchers.IO) {

            val client = mqttAndroidClient ?: return@withContext false

            // 检查客户端连接状态
            if (!client.isConnected) {
                LogUtils.d("MQTT客户端未连接，无法订阅")
                return@withContext false
            }

            return@withContext try {
                // 执行订阅操作
                val token = client.subscribe(topic, qos)

                // 设置订阅结果回调
                token.actionCallback = object : IMqttActionListener {
                    override fun onSuccess(asyncActionToken: IMqttToken) {
                        // 将主题和QoS添加到订阅列表中
                        subscriptions[topic] = qos
                        LogUtils.d("订阅成功 $topic")
                    }

                    override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                        // 订阅失败时从订阅列表中移除
                        subscriptions.remove(topic)
                        LogUtils.d("订阅失败 $topic")
                    }
                }
                // 等待订阅操作完成
                token.waitForCompletion()
                true
            } catch (e: Exception) {
                // 订阅过程中发生异常，返回false
                LogUtils.d("订阅异常: ${e.toString()}")
                false
            }
        }

    /**
     * 取消订阅指定主题
     * 停止接收指定主题的消息
     *
     * @param topic 要取消订阅的主题
     * @return 取消订阅是否成功
     */
    suspend fun unsubscribe(topic: String): Boolean = withContext(Dispatchers.IO) {
        // 获取MQTT客户端实例，如果为空则返回false
        val client = mqttAndroidClient ?: return@withContext false
        // 检查客户端连接状态
        if (!client.isConnected) {
            LogUtils.d("MQTT客户端未连接，无法订阅")
            return@withContext false
        }
        return@withContext try {
            // 执行取消订阅操作
            val token = client.unsubscribe(topic)
            // 设置取消订阅结果回调
            token.actionCallback = object : IMqttActionListener {
                /**
                 * 取消订阅成功回调
                 */
                override fun onSuccess(asyncActionToken: IMqttToken) {
                    // 从订阅列表中移除主题
                    subscriptions.remove(topic)
                    LogUtils.d("取消订阅成功 $topic")
                }

                /**
                 * 取消订阅失败回调
                 *
                 * @param asyncActionToken 异步操作令牌
                 * @param exception 取消订阅失败异常
                 */
                override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                    // 取消订阅失败，保留在订阅列表中
                    LogUtils.d("取消订阅失败 $topic")
                }
            }

            // 等待取消订阅操作完成
            token.waitForCompletion()
            true
        } catch (e: Exception) {
            // 取消订阅过程中发生异常，返回false
            LogUtils.d("取消订阅异常: ${e.toString()}")
            false
        }
    }

    /**
     * 取消所有订阅
     * 停止接收所有已订阅主题的消息
     *
     * @return 取消所有订阅是否成功
     */
    suspend fun unsubscribeAll(): Boolean = withContext(Dispatchers.IO) {
        // 获取所有已订阅的主题数组
        val topics = subscriptions.keys.toTypedArray()
        // 如果没有订阅任何主题，直接返回true
        if (topics.isEmpty()) return@withContext true

        // 获取MQTT客户端实例，如果为空则返回false
        val client = mqttAndroidClient ?: return@withContext false

        if (!client.isConnected) {
            LogUtils.d("MQTT客户端未连接，无法订阅")
            return@withContext false
        }

        return@withContext try {
            // 执行批量取消订阅操作
            val token = client.unsubscribe(topics)
            // 设置取消订阅结果回调
            token.actionCallback = object : IMqttActionListener {
                /**
                 * 取消订阅成功回调
                 */
                override fun onSuccess(asyncActionToken: IMqttToken) {
                    // 清空所有订阅记录
                    subscriptions.clear()
                }

                /**
                 * 取消订阅失败回调
                 *
                 * @param asyncActionToken 异步操作令牌
                 * @param exception 取消订阅失败异常
                 */
                override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
                    // 处理失败情况，保留订阅记录
                }
            }

            // 等待取消订阅操作完成
            token.waitForCompletion()
            true
        } catch (e: Exception) {
            // 取消订阅过程中发生异常，返回false
            false
        }
    }

    /**
     * 重新订阅所有主题（在重连后调用）
     * 在MQTT客户端重连成功后，重新订阅之前的所有主题
     */
    private fun resubscribeTopics() {
        // 在IO线程中执行重新订阅操作
        CoroutineScope(Dispatchers.IO).launch {
            // 遍历所有已订阅的主题并重新订阅
            subscriptions.forEach { (topic, qos) ->
                subscribe(topic, qos)
            }
        }
    }

    /**
     * 发布消息
     * 向指定主题发送文本消息
     *
     * @param topic 发布消息的目标主题
     * @param payload 消息内容
     * @param qos 服务质量等级
     * @param retained 是否保留消息
     * @return 消息发布是否成功
     */
    suspend fun publish(
        topic: String,
        payload: String,
        qos: Int = QoS.AtMostOnce.value,
        retained: Boolean = false
    ): Boolean = withContext(Dispatchers.IO) {
        // 获取MQTT客户端实例，如果为空则返回false
        val client = mqttAndroidClient ?: return@withContext false
        // 检查客户端是否已连接，未连接则返回false
        if (!client.isConnected) return@withContext false

        return@withContext try {
            // 创建MQTT消息对象
            val message = MqttMessage(payload.toByteArray(Charsets.UTF_8)).apply {
                // 设置服务质量等级
                this.qos = qos
                // 设置是否保留消息
                isRetained = retained
            }

            // 执行消息发布操作
            val token = client.publish(topic, message)
            // 等待发布操作完成
            token.waitForCompletion()
            true
        } catch (e: Exception) {
            // 发布过程中发生异常，返回false
            false
        }
    }

    /**
     * 发布对象消息（使用Moshi序列化）
     * 将对象序列化为JSON后发布到指定主题
     *
     * @param T 泛型参数，表示要发布的对象类型
     * @param topic 发布消息的目标主题
     * @param data 要发布的对象数据
     * @param qos 服务质量等级
     * @param retained 是否保留消息
     * @return 消息发布是否成功
     */
    suspend inline fun <reified T> publishObject(
        topic: String,
        data: T,
        qos: Int = QoS.AtMostOnce.value,
        retained: Boolean = false
    ): Boolean = withContext(Dispatchers.IO) {
        try {
            // 获取对应类型的JSON适配器
            val jsonAdapter = moshi.adapter<T>(T::class.java)
            // 将对象序列化为JSON字符串
            val json = jsonAdapter.toJson(data)
            // 发布JSON字符串消息
            publish(topic, json, qos, retained)
        } catch (e: Exception) {
            // 序列化或发布过程中发生异常，返回false
            false
        }
    }

    /**
     * 断开连接
     * 关闭与MQTT服务器的连接
     */
    fun disconnect() {
        // 设置不继续重连标志
        shouldReconnect = false
        // 获取MQTT客户端实例，如果为空则直接返回
        val client = mqttAndroidClient ?: return

        try {
            // 检查客户端是否已连接
            if (client.isConnected) {
                // 执行断开连接操作
                client.disconnect()
            }
            // 更新连接状态为已断开
            _connectionState.value = MqttConnectionState.DISCONNECTED
        } catch (e: Exception) {
            // 忽略断开连接时的异常
        }
    }

    /**
     * 检查是否已连接
     * 判断当前MQTT客户端是否与服务器建立连接
     *
     * @return 是否已连接
     */
    fun isConnected(): Boolean {
        // 返回MQTT客户端的连接状态
        return mqttAndroidClient?.isConnected ?: false
    }

    /**
     * 获取当前缓冲消息数量
     * 查询断开连接期间缓存的消息数量
     *
     * @return 缓冲消息数量
     */
    fun getBufferedMessageCount(): Int {
        // 返回MQTT客户端的缓冲消息数量
        return mqttAndroidClient?.bufferedMessageCount ?: 0
    }
}